package zdream.pmfield.stage.buff.ailment;

import java.util.Objects;

import zdream.pmfield.data.foe.BStat;
import zdream.pmfield.data.foe.MoveAdditionalEffects;
import zdream.pmfield.data.foe.MoveTemplate;
import zdream.pmfield.data.foe.MoveType;
import zdream.pmfield.stage.action.BuffAction;
import zdream.pmfield.stage.buff.foe.FoeStageBuff;
import zdream.pmfield.stage.director.AttackContext;
import zdream.pmfield.stage.director.HookType;
import zdream.pmfield.stage.director.Hooks;
import zdream.pmfield.stage.message.FoeReceiveDamageMessage;
import zdream.pmfield.stage.object.FoeActor;
import zdream.pmfield.stage.object.StageMove;

/**
 * <p>烧伤 / 灼伤状态, 和 Pokemon 里面的不一样.
 * <p>这里修改为, 每次 {@link FoeActor} 进行攻击之后触发 (不攻击不会触发)
 * <p>对进行了接触攻击的 {@link FoeActor} 每次受到 1/10 或更少的伤害,
 * 这里做出的设计是, HP 最大值越大, 伤血越大, 但是占 HP 总量越少.
 * 烧伤无视护盾.
 * </p>
 *
 * <li> HP 最大值在 30 以下时, 伤 1/10, 数值向上取整;
 * <li> HP 最大值等于 30 时, 每次伤 3; (10.0%)
 * <li> HP 最大值在 30 以上时, 按照下面的公式计算;
 * <li> 按照下面公式, HP 最大值等于 90 时, 每次伤 5; (5.6%)
 * <li> 按照下面公式, HP 最大值等于 150 时, 每次伤 7; (4.7%)
 * <li> 按照下面公式, HP 最大值等于 210 时, 每次伤 9; (4.3%)
 * </li>
 *
 * <pre>
 * damage = Math.ceil((hpMax + 60) / 30)
 * </pre>
 *
 * <p>烧伤额外降低物理技能（对别人）的伤害, 降至原来的 75%
 *
 * <p>
 * 烧伤效果能自然恢复, 3 轮之后投骰子判断是否恢复.
 * 主要通过 {@link zdream.pmfield.data.foe.VStat#LUCK} 来决定是否恢复.
 * </p>
 *
 * @author Zdream
 * @date 2023-11-25
 * @since 0.0.1
 */
public class BurnBuff extends FoeStageBuff {
	public static final String CATEGORY = MoveAdditionalEffects.E_BURN;
	public final BuffSpan span;

	public BurnBuff(FoeActor foe) {
		super(foe);
		span = new DurationBuffSpan()
				.withMinDuration(3)
				.withStartTime(now());
	}

	@Override
	public String getCategory() {
		return CATEGORY;
	}

	@Override
	public String toString() {
		return "烧伤";
	}

	@Override
	public void init() {
		preventSameBuffBeforeFoeReceiveBuff();
		onHook(HookType.BEFORE_FOE_TURN_START)
				.handle(span::checkIsNeedCured)
				.buildAndSave();
		onHook(HookType.AFTER_EXECUTE_ATTACK)
				.handle(this::putBurnDamage)
				.buildAndSave();
		onHook(HookType.ON_CHECK_TYPE_FIXED)
				.handle(this::reducePhysicalDamage)
				.buildAndSave();
	}

	public void putBurnDamage(Hooks.HookContext rawContext) {
		Hooks.AttackHookContext context = rawContext.as();
		if (shouldSkipBurnDamageForMove(context.attackContext)) {
			return;
		}

		int hpMax = target.bStats[BStat.HP.ordinal()];

		int damage = (hpMax <= 30) ?
				(int) Math.ceil((hpMax) / 10f) :
				(int) Math.ceil((hpMax + 60f) / 30);

		putBuffActionNow(buildTask(damage));
	}

	public static boolean shouldSkipBurnDamageForMove(AttackContext context) {
		final StageMove move = context.currentMove;

		if (move.hasTag(MoveAdditionalEffects.T_CHARGING)) {
			// 蓄力技能的蓄力阶段不造成烧伤伤害
			return Objects.equals(move.title, MoveTemplate.SUB_TITLE_CHARGING);
		}

		return context.actionPoints == 0;
	}

	private static String buildTask(int damage) {
		return String.format("damage=%d", damage);
	}

	private static int parseTask(String task) {
		return Integer.parseInt(task.split("=")[1]);
	}

	@Override
	public void doTask(BuffAction action) {
		int damage = parseTask(action.task);

		final FoeReceiveDamageMessage damageMessage = new FoeReceiveDamageMessage(target)
				.withDamage(damage)
				.withTitle(CATEGORY)
				.withHpFrom(target);

		target.receiveDamageIgnoreShield(damage);
		pushMessage(damageMessage.withHpTo(target));
	}

	private void reducePhysicalDamage(Hooks.HookContext rawContext) {
		final Hooks.AttackHookContext context = rawContext.as();
		final AttackContext attackContext = context.attackContext;

		if (attackContext.attacker != this.target) {
			return;
		}
		if (attackContext.currentMove.attackerUsing != MoveType.PHYSICAL) {
			return;
		}
		attackContext.effectFixed *= 0.75f;
	}
}
